package io.gitee.mingbaobaba.security.oauth2.repository;

import io.gitee.mingbaobaba.security.core.utils.DateUtil;
import io.gitee.mingbaobaba.security.core.utils.JsonUtil;
import io.gitee.mingbaobaba.security.oauth2.domain.SecurityOauth2Client;
import io.gitee.mingbaobaba.security.oauth2.domain.SecurityOauth2Details;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;


/**
 * <p>Repository默认实现</p>
 *
 * @author yingsheng.ye
 * @version 1.0.0
 * @since 2023/9/10 4:40
 */
public class SecurityOauth2RepositoryDefaultImpl implements SecurityOauth2Repository {

    private static final String CREATE_TIME_KEY = "createTime";

    private static final String TIME_OUT_KEY = "timeOut";

    private static final String OBJECT_KEY = "storeObj";

    private static final AtomicReference<Map<String, Map<String, Object>>> authorizationCodeMap = new AtomicReference<>(new ConcurrentHashMap<>());
    private static final AtomicReference<Map<String, Map<String, Object>>> refreshTokenMap = new AtomicReference<>(new ConcurrentHashMap<>());
    private static final AtomicReference<Map<String, Map<String, Object>>> accessTokenMap = new AtomicReference<>(new ConcurrentHashMap<>());

    @Override
    public void saveAuthorizationCode(String authorizationCode, SecurityOauth2Client securityClientMode, long timeout) {
        Map<String, Object> map = new HashMap<>();
        map.put(CREATE_TIME_KEY, DateUtil.formatDateTime.apply(new Date()));
        map.put(TIME_OUT_KEY, timeout);
        map.put(OBJECT_KEY, securityClientMode);
        authorizationCodeMap.get().put(authorizationCode, map);
    }

    @Override
    public SecurityOauth2Client getClientModelByAuthorizationCode(String authorizationCode) {
        Map<String, Object> map = authorizationCodeMap.get().get(authorizationCode);
        if (Objects.isNull(map)) {
            return null;
        }
        long timeout = (long) map.get(TIME_OUT_KEY);
        String createTime = (String) map.get(CREATE_TIME_KEY);
        if (DateUtil.strToLocalDateTime.apply(createTime).plusSeconds(timeout).isBefore(LocalDateTime.now())) {
            authorizationCodeMap.get().remove(authorizationCode);
            return null;
        }
        return (SecurityOauth2Client) map.get(OBJECT_KEY);
    }

    @Override
    public void removeAuthorizationCode(String authorizationCode) {
        authorizationCodeMap.get().remove(authorizationCode);
    }

    @Override
    public boolean saveOauth2TokenDetails(SecurityOauth2Details securityOauth2Details, Long timeout) {
        Map<String, Object> refreshMap = new HashMap<>();
        refreshMap.put(CREATE_TIME_KEY, DateUtil.formatDateTime.apply(new Date()));
        refreshMap.put(TIME_OUT_KEY, timeout);
        refreshMap.put(OBJECT_KEY, securityOauth2Details.getAccessToken());
        refreshTokenMap.get().put(securityOauth2Details.getRefreshToken(), refreshMap);

        Map<String, Object> accessMap = new HashMap<>();
        accessMap.put(CREATE_TIME_KEY, DateUtil.formatDateTime.apply(new Date()));
        accessMap.put(TIME_OUT_KEY, timeout);
        accessMap.put(OBJECT_KEY, JsonUtil.objectToJsonStr(securityOauth2Details));
        accessTokenMap.get().put(securityOauth2Details.getAccessToken(), accessMap);
        return true;
    }

    @Override
    public SecurityOauth2Details getOauth2DetailsByAccessToken(String accessToken) {
        Map<String, Object> map = accessTokenMap.get().get(accessToken);
        if (Objects.isNull(map)) {
            return null;
        }
        return JsonUtil.jsonStrToObject((String) map.get(OBJECT_KEY), SecurityOauth2Details.class);
    }

    @Override
    public String accessTokenByRefreshToken(String refreshToken) {
        Map<String, Object> map = refreshTokenMap.get().get(refreshToken);
        if (Objects.isNull(map)) {
            return null;
        }
        String createTime = (String) map.get(CREATE_TIME_KEY);
        long timeout = (long) map.get(TIME_OUT_KEY);
        if (DateUtil.strToLocalDateTime.apply(createTime).plusSeconds(timeout).isBefore(LocalDateTime.now())) {
            authorizationCodeMap.get().remove(refreshToken);
            return null;
        }
        return (String) map.get(OBJECT_KEY);
    }

    @Override
    public Long refreshTokenTimeOut(String refreshToken) {
        Map<String, Object> map = refreshTokenMap.get().get(refreshToken);
        if (Objects.isNull(map)) {
            return null;
        }
        String createTime = (String) map.get(CREATE_TIME_KEY);
        long timeout = (long) map.get(TIME_OUT_KEY);
        long second = DateUtil.strToLocalDateTime.apply(createTime).plusSeconds(timeout)
                .toEpochSecond(ZoneOffset.ofHours(8))
                - LocalDateTime.now().toEpochSecond(ZoneOffset.ofHours(8));
        return second > 0 ? second : 0L;
    }
}
